<?php

/**
 * Maikuro Framework
 *
 * LICENSE:
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * The license is bundled with this package in the file LICENSE.txt.
 * You may also obtain a copy of the License at this URL:
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * @copyright  Copyright (c) 2010, Jonathan Hedrén
 * @author     Jonathan Hedrén <jonathan.hedren@gmail.com>
 * @license    http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
 */

/**
 * @see IDependencyInjector
 */
require_once 'IDependencyInjector.php';

/**
 * @see IObjectFactory
 */
require_once 'IObjectFactory.php';

/**
 * 
 */
class StandardDependencyInjector implements IDependencyInjector
{
    /**
     *
     * @var array
     */
    protected $dependencyMappings = array();

    /**
     *
     * @var array
     */
    protected $instances = array();

    /**
     *
     * @var IObjectFactory
     */
    protected $objectFactory;

    /**
     *
     * @param string $className
     * @return mixed
     */
    protected function instance($className)
    {
        if ($this->objectFactory == null) {
            return new $className();
        }
        return $this->objectFactory->instance($className);
    }

    /**
     *
     * @param string $className
     * @param boolean $keepInstance
     * @return mixed
     */
    public function instanceWithDependencies($className)
    {
        if (!isset($this->instances[$className])) {

            $reflectionClass = new ReflectionClass($className);
            $dependencies = array();
            
            if (isset($this->dependencyMappings[$className])) {
                foreach ($this->dependencyMappings[$className] as $dependencyClass) {
                    $dependencies[] = $this->instance($dependencyClass);
                }
            } else {

                $reflectionMethod = $reflectionClass->getConstructor();
                if ($reflectionMethod == null) {
                    $this->instances[$className] = new $className();
                    return $this->instances[$className];
                } else {

                    $reflectionParameters = $reflectionMethod->getParameters();
                    foreach ($reflectionParameters as $reflectionParameter) {
                        $reflectionParameterClass = $reflectionParameter->getClass();
                        if ($reflectionParameterClass == null) {
                            throw new Exception('Dependency injection parameters must be typed.');
                        }
                        $dependencies[] = $this->instance($reflectionParameterClass->getName());

                    }
                }
            }
            $this->instances[$className] = $reflectionClass->newInstanceArgs($dependencies);
        }
        return $this->instances[$className];
    }

    /**
     *
     * @param string $className
     * @param array $dependencyClassNames
     * @return ObjectFactory
     */
    public function setDependencyMapping($className, array $dependencyClassNames)
    {
        $this->dependencyMappings[$className] = $dependencyClassNames;
        return $this;
    }

    /**
     *
     * @param ObjectFactory $objectFactory
     * @return DependencyInjector 
     */
    public function setObjectFactory(IObjectFactory $objectFactory)
    {
        $this->objectFactory = $objectFactory;
        return $this;
    }
}